﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using WorkItemAnalyser.Core;
using WorkItemAnalyser.Core.Parsers;

namespace WorkItemAnalyser.Console
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var argDictionary = ParseArguments(args);

            if (ValidateArgs(argDictionary))
            {
                var rules = ReadRulesFile();

                using (
                    var projectCollection =
                        TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(Settings.Default.ServerURL)))
                {
                    var changesets = GetCandidateChangesets(projectCollection, argDictionary["/source"], argDictionary["/target"]);
                    var workItems = GetWorkItems(changesets);

                    if (workItems.Count() == 0)
                    {
                        System.Console.WriteLine("Error: No workitems found!");
                        if (Debugger.IsAttached) System.Console.ReadKey();
                        
                        return;
                    }

                    var ruleExecutor = new RuleExecutor<string>(new TextRuleParser());
                    var results = ruleExecutor.Execute(rules, workItems);

                    foreach (var result in results.Where(result => !result.IsValid))
                    {
                        System.Console.WriteLine("Error: {0}", result.Message);
                    }
                }
            }

            System.Console.WriteLine("Analysis complete...");
            if (Debugger.IsAttached) System.Console.ReadKey();
        }

        private static IEnumerable<WorkItem> GetWorkItems(Changeset[] changesets)
        {
            IEnumerable<WorkItem> workItems = new Collection<WorkItem>();

            foreach (var changeset in changesets)
            {
                workItems = workItems.Union(changeset.WorkItems);
            }

            return workItems;
        }

        private static string ReadRulesFile()
        {
            return File.ReadAllText(Settings.Default.RulesFile);
        }

        private static Changeset[] GetCandidateChangesets(TfsTeamProjectCollection projectCollection, string sourceBranch, string targetBranch)
        {
            var versionControl = projectCollection.GetService<VersionControlServer>();
            var mergeCandidates = versionControl.GetMergeCandidates(sourceBranch, targetBranch, RecursionType.Full);

            return mergeCandidates.Select(mergeCandidate => mergeCandidate.Changeset).ToArray();
        }

        private static void WriteHelp()
        {
            System.Console.WriteLine("Work Item Change Set Analyser");
            System.Console.WriteLine("------------------------------");
            System.Console.WriteLine("Usage: This tool is used to determine that all code to be");
            System.Console.WriteLine("       promoted has a work item associated in the correct state");
            System.Console.WriteLine("");
            System.Console.WriteLine("Args: /source - The source branch for a merge operation");
            System.Console.WriteLine("      /target - The target branch for a merge operation");
            System.Console.WriteLine("      /rules - The path to a file listing the rules to enforce");
            System.Console.WriteLine("");
            System.Console.WriteLine(
                "Example: wicsa.exe /source $/WICSA/branches/dev/ /target $/WICSA/trunk/ /rules C:\temp\rules.txt");
        }

        private static bool ValidateArgs(Dictionary<string, string> argDictionary)
        {
            var argsValid = true;

            if ((argDictionary == null) || (argDictionary.Count == 0))
            {
                WriteHelp();
                return false;
            }

            if (!argDictionary.ContainsKey("/source") || !argDictionary.ContainsKey("/target"))
            {
                System.Console.WriteLine("Error: A source and target branch must be provided");
                argsValid = false;
            }

            if (!argDictionary.ContainsKey("/rules"))
            {
                System.Console.WriteLine("Error: A rules listing must be provided");
                argsValid = false;
            }

            return argsValid;
        }

        private static Dictionary<string, string> ParseArguments(string[] args)
        {
            var key = string.Empty;
            var dictionary = new Dictionary<string, string>();

            foreach (var arg in args)
            {
                if (arg.Contains("/") && (!arg.Contains("$")))
                {
                    if (string.IsNullOrEmpty(key))
                    {
                        key = arg.ToLowerInvariant();
                    }
                }
                else
                {
                    if (!string.IsNullOrEmpty(key))
                    {
                        dictionary.Add(key, arg);
                        key = string.Empty;
                    }
                }
            }

            return dictionary;
        }
    }
}